﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Romeo.Data
{
    public class MinimaxEvaluator : Evaluator
    {
        private int _Depth;
        private Evaluator _MidgameEvaluator;
        private Evaluator _EndgameEvaluator;

        private int _maxmoves;
        private int[] _moveset = new int[64];

        private int[] _Moves = new int[] {
            /* corners */11, 18, 81, 88,
            /* border out */13, 31, 16, 61, 83, 38, 86, 68,
            /* border in */14, 41, 15, 51, 84, 48, 85, 58,
            /* inner ring */33, 63, 66, 36, 34, 43, 35, 53, 64, 46, 65, 56,
            /* outer ring */32, 23, 62, 26, 37, 73, 67, 76, 24, 42, 74, 47, 25, 52, 75, 57,
            /* border worst */12, 21, 17, 71, 28, 82, 78, 87,
            /* worst */22, 77, 72, 27 };

        public MinimaxEvaluator (int aDepth, Evaluator aMidgameEvaluator, Evaluator aEndgameEvaluator)
        {
            _Depth = aDepth;
            _MidgameEvaluator = aMidgameEvaluator;
            _EndgameEvaluator = aEndgameEvaluator;
        }

        public MinimaxEvaluator (int aDepth, Evaluator aMidgameEvaluator)
            : this (aDepth, aMidgameEvaluator, EndgameEvaluator.Singleton)
        {
        }

        protected int Maximize (Board aBoard, int aDepth, bool aPassed, int aMin, int aMax, ref int aKiller)
        {
            if (aBoard._Remaining == 0)
            {
                EvaluatedEndPositions++;
                return _EndgameEvaluator.Evaluate (aBoard, aMin);
            }

            int bestscore = Int32.MinValue;
            int k = 0, oldk=aKiller;
            for (int i = -1; i < _maxmoves; i++)
            {
                int p;
                if (i == -1)
                {
                    p = oldk;
                    if (p == 0)
                        continue;
                }
                else
                {
                    p = _moveset[i];
                    if (p == oldk)
                        continue;
                }
                Move m = aBoard.PlayOn (p, Board.Computer);
                if (m != null)
                {
                    EvaluatedMoves++;
                    int score = Minimize (aBoard, aDepth - 2, false, aMin, aMax, ref k);
                    if (score > bestscore)
                    {
                        bestscore = score;
                        if (bestscore > aMin)
                        {
                            aMin = bestscore;
                            aKiller = p;
                        }
                    }
                    aBoard.Undo (m);
                    aBoard.RecycleMove(m);
                }
                if (bestscore > aMax)
                {
                    AlphaBetaPrunes++;
                    return bestscore;
                }
            }

            if (bestscore == Int32.MinValue)
            {
                if (aPassed)
                {
                    EvaluatedEndPositions++;
                    return _EndgameEvaluator.Evaluate (aBoard, aMin);
                }
                else
                {
                    return Minimize (aBoard, aDepth - 1, true, aMin, aMax, ref k);
                }
            }

            return bestscore;
        }

        protected int Minimize (Board aBoard, int aDepth, bool aPassed, int aMin, int aMax, ref int aKiller)
        {
            if (aBoard._Remaining == 0)
            {
                EvaluatedEndPositions++;
                return _EndgameEvaluator.Evaluate (aBoard, aMin);
            }

            int bestscore = Int32.MaxValue;
            int k = 0, oldk=aKiller;
            for (int i = -1; i < _maxmoves; i++)
            {
                int p;
                if (i == -1)
                {
                    p = oldk;
                    if (p == 0)
                        continue;
                }
                else
                {
                    p = _moveset[i];
                    if (p == oldk)
                        continue;
                }
                Move m = aBoard.PlayOn (p, Board.Opponent);
                if (m != null)
                {
                    EvaluatedMoves++;
                    int score;
                    if (aDepth > 0)
                    {
                        score = Maximize (aBoard, aDepth - 2, false, aMin, aMax, ref k);
                    }
                    else if (aBoard._Remaining < 4)
                    {
                        // TODO : Make the "4" a parameter ?
                        EvaluatedEndPositions++;
                        score = _EndgameEvaluator.Evaluate (aBoard, aMin);
                    }
                    else
                    {
                        EvaluatedMidPositions++;
                        score = _MidgameEvaluator.Evaluate (aBoard, aMin);
                    }
                    if (score < bestscore)
                    {
                        bestscore = score;
                        if (bestscore < aMax)
                        {
                            aMax = bestscore;
                            aKiller = p;
                        }
                    }
                    aBoard.Undo (m);
                    aBoard.RecycleMove (m);
                }
                if (bestscore < aMin)
                {
                    AlphaBetaPrunes++;
                    return bestscore;
                }
            }

            if (bestscore == Int32.MaxValue)
            {
                if (aPassed || aDepth <= 0)
                {
                    EvaluatedEndPositions++;
                    return _EndgameEvaluator.Evaluate (aBoard, aMin);
                }
                else
                {
                    return Maximize (aBoard, aDepth - 1, true, aMin, aMax, ref k);
                }
            }

            return bestscore;
        }

        public override int Evaluate (Board aBoard, int aMinScore)
        {
            _maxmoves = 0;
            foreach (int m in _Moves)
            {
                if (aBoard[m] == Board.Empty)
                {
                    _moveset[_maxmoves++] = m;
                }
            }
            int k = 0;
            return Minimize (aBoard, _Depth * 4, false, aMinScore, Int32.MaxValue, ref k);
        }

    }
}
